Skip to content

feat(push): add final status block and structured JSON output#1199

Open
l2ysho wants to merge 5 commits into
masterfrom
1136-improve-apify-push-final-status-and-json-output-for-agents2
Open

feat(push): add final status block and structured JSON output#1199
l2ysho wants to merge 5 commits into
masterfrom
1136-improve-apify-push-final-status-and-json-output-for-agents2

Conversation

@l2ysho

@l2ysho l2ysho commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Covers the status-mapping contract introduced by the new push status block / JSON output

  • Add test/local/commands/push.test.ts — 10 cases over every branch: SUCCEEDED, READY/RUNNING (fire-and-forget → exit 0), ABORTED/ABORTING, TIMED_OUT/TIMING_OUT,
    FAILED, and the unknown-status fallthrough. Plus an assertion that errorMessage is present on failures and absent on success/running.

Closes #1136

Rework the end of `apify push` so agents can distinguish source-upload
success from final cloud-build success (#1136).

- `apify push` prints a final result block (result, upload, build,
  IDs, URLs, exit code, and a failure reason log tail).
- `apify push --json` returns a structured result object with
  `ok`, `operation`, `actor`, `build`, optional `error`, and `exitCode`.
- Failed, timed-out, and aborted builds return non-zero exit codes;
  `ok` mirrors command success.

Closes #1136

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@l2ysho l2ysho self-assigned this Jun 15, 2026
@github-actions github-actions Bot added this to the 143rd sprint - Tooling team milestone Jun 15, 2026
@github-actions github-actions Bot added the t-tooling Issues with this label are in the ownership of the tooling team. label Jun 15, 2026
@l2ysho l2ysho added t-dx Issues owned by the DX team. and removed t-tooling Issues with this label are in the ownership of the tooling team. labels Jun 15, 2026
@l2ysho l2ysho requested a review from DaveHanns June 15, 2026 22:13
@github-actions github-actions Bot added the tested Temporary label used only programatically for some analytics. label Jun 15, 2026
@l2ysho l2ysho marked this pull request as ready for review June 15, 2026 22:17
@l2ysho l2ysho requested a review from vladfrangu as a code owner June 15, 2026 22:17
// How many trailing log lines to surface as the failure reason.
const BUILD_LOG_TAIL_LINES = 10;

interface PushResult {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for some reason we use 'typescript/consistent-type-definitions': ['error', 'interface'], (even before oxlint migration). We use @apify/oxlint-config but there is few additional changes in it, I guess lets align this with Apify Coding Standards in separate PR (too much noise to change all interfaces to types here).

@vladfrangu do you have some context about this type/interface decision ?

build: { id: string; number: string; status: string; url: string };
error?: { phase: 'build'; message: string; logTail: string[] };
exitCode: number;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: to avoid type-checking for optional error, we could leverage discriminated union here:

type PushResultBase = {
	operation: 'push';
	actor: { id: string; url: string };
	build: { id: string; number: string; status: string; url: string };
	exitCode: number;
};
type PushSuccess = PushResultBase & {
	ok: true;
};
type PushFailure = PushResultBase & {
	ok: false;
	error: { phase: 'build'; message: string; logTail: string[] };
};
type PushResult = PushSuccess | PushFailure;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +75 to +77
case ACTOR_JOB_STATUSES.READY:
case ACTOR_JOB_STATUSES.RUNNING:
return { resultLabel: 'RUNNING', exitCode: 0, ok: true };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: might be a bit misleading. Why don't we have resultLabel: 'READY'?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I thinking about this now, maybe it would be better to READY and RUNNING status resolve to IN_PROGRESS result label? as READY + ok: true feels like DONE at first glance but actually it is waiting to be processed by worker

exitCode: number;
ok: boolean;
errorMessage?: string;
} {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the return type would deserve to be extracted into its own type.

exitCode: CommandExitCodes.BuildTimedOut,
ok: false,
errorMessage: 'Build timed out',
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: similarly as above, why don't we have distinguished resultLabel + errorMessage for both ABORTED and ABORTING (respectively TIMED_OUT and TIMING_OUT)?

return { resultLabel: 'SUCCEEDED', exitCode: 0, ok: true };
case ACTOR_JOB_STATUSES.READY:
case ACTOR_JOB_STATUSES.RUNNING:
return { resultLabel: 'RUNNING', exitCode: 0, ok: true };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe the exitCode should be optional and not returned in case the process hasn't ended yet. Technically, at a time of READY and RUNNING, there is no exitCode yet. ok is alright, as at that time the process is truly ok.

errorMessage: 'Build timed out',
};
default:
return { resultLabel: 'FAILED', exitCode: CommandExitCodes.BuildFailed, ok: false, errorMessage: 'Build failed' };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: not super sure about the default. If there is ever a new state, it might fall back to FAILED even if it is not.

const buildStatus = build.status as string;
const outcome = resolvePushOutcome(buildStatus);

const actorUrl = `https://console.apify.com${redirectUrlPart}/actors/${build.actId}`;

@DaveHanns DaveHanns Jun 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: it is alright now, but I noticed we are hardcoding apify domain (api, console, ...) a lot in the cli. At some point we should develop a registry of domains so that we have a single source of truth.

Ignore as it is out of scope of this PR.

} else {
error({ message: 'Build failed!' });
process.exitCode = CommandExitCodes.BuildFailed;
const lines = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: do we already implement same style logging in case the upload itself fails?

@DaveHanns DaveHanns left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few nits and suggestions. Otherwise looks good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t-dx Issues owned by the DX team. tested Temporary label used only programatically for some analytics.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve apify push final status and JSON output for agents

3 participants